package com.spring.remote;

import java.lang.reflect.Method;
import java.security.KeyStore.Entry;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.beanutils.ConvertUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.ObjectUtils;
import org.springframework.util.PathMatcher;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

import com.spring.annoation.AnnoationInit;
import com.spring.annoation.Interceptorn;
import com.spring.http.HttpContext;
import com.spring.http.HttpUtils;
import com.spring.http.ReturnData;
import com.spring.listener.SpringContext;
import com.spring.netty.HttpServerHandler;
import com.spring.task.TaskDispatcher;
import com.spring.task.TaskEvent;
import com.spring.util.MapToBean;
import com.spring.util.TypeToolsGenerics;

/**
 * 添加路由配置
 * 
 * @author Administrator
 *
 */
public class RemoteFactory {
	private final static Logger logger = LogManager.getLogger(HttpServerHandler.class.getName());

	public static Map<String, Object> mapResult = new HashMap<String, Object>();// 缓存结果
	public static TaskEvent event = new TaskEvent();

	// 反射执行方法
	public static void getInvoke(HttpContext context) throws Exception {
		logger.debug("调用之前" + context);
		Map<String, Invoke> mapUri = SpringContext.mapUri;
		String initUri = context.getUri();
		String uri = context.getUri();
		// 找到匹配的url
		Invoke invoke = mapUri.get(getmatch(uri, mapUri));
		logger.debug("根据url" + uri + "找到的method" + invoke);
		if (invoke != null) {
			String methodName = invoke.getMethod();
			Object object = invoke.getSeviceObject();
			// 执行反射之前，来判断结果是否存在
			if (mapResult.get(initUri) != null) {
				typeSelect(mapResult.get(initUri), context);
				event.setContext(context);
				TaskDispatcher.submitReponse(event);
				return;
			}
			Method method = ReflectionUtils.findMethod(object.getClass(), methodName, invoke.getParamTypes());
			// 循环遍历参数类型，将map中的值转成bean
			List<String> paramterName = getParamterName(object.getClass(), methodName);
			Object[] parm = createParm(invoke.getParamTypes(), paramterName, context.getParams(), context);
			Object invokeMethod = ReflectionUtils.invokeMethod(method, object, parm);
			mapResult.put(initUri, invokeMethod);
			// 根据类型，返回结果
			typeSelect(invokeMethod, context);
			event.setContext(context);
			// 返回视图还是json

			if (invoke.getModeView() != null) {
				if (StringUtils.isEmpty(invoke.getModeView().getName())) {
					invoke.getModeView().setName((String) invokeMethod);
					context.setRes(HttpUtils.errorSystem(ReturnData.getData(200, (String) invokeMethod, null, "2")));
				}
			}
			TaskDispatcher.submitReponse(event);
			return;
		} else {
			context.setRes(HttpUtils.errorSystem(ReturnData.getData(500, "not found", null, "1")));
			HttpServerHandler.sendHttpResponse(context.getCtx(), context.getReq(), context.getRes());
			return;
		}

	}

	/**
	 * 匹配找到的url
	 * 
	 * @param uri
	 * @param map2
	 * @return
	 */
	private static String getmatch(String uri, Map<String, Invoke> map) {
		Set<String> keySet = map.keySet();
		for (String entry : keySet) {
			PathMatcher matcher = new AntPathMatcher();
			boolean result = matcher.match(entry, uri);
			if (result) {
				return entry;
			}
		}
		return "";

	}

	/**
	 * 根据类型来判断返回类型
	 * 
	 * @param invokeMethod
	 * @param context
	 * @throws Exception
	 */
	private static void typeSelect(Object invokeMethod, HttpContext context) throws Exception {
		if (invokeMethod instanceof String) {
			context.setRes(HttpUtils.errorSystem(ReturnData.getData(200, (String) invokeMethod, null, "1")));
		} else if (invokeMethod instanceof Integer) {
			context.setRes(HttpUtils.errorSystem(ReturnData.getData(200, (Integer) invokeMethod, null, "1")));
		} else {
			context.setRes(HttpUtils.errorSystem(ReturnData.getData(200, invokeMethod, null, "1")));
		}
	}

	// 参数注入
	private static Object[] createParm(Class<?>[] paramTypes, List<String> paramterName, Map<String, Object> map,
			HttpContext context) {
		List<Class<?>> asList = Arrays.asList(paramTypes);
		List<Object> objects = new LinkedList<Object>();
		Object[] array = new Object[objects.size()];

		for (int i = 0; i < asList.size(); i++) {
			try {
				// 过滤context，不进行转换
				Class<?> entry = asList.get(i);
				if (TypeToolsGenerics.contains(entry.getTypeName())) {// 如果是基本数据类型(包括String)
					// 如果不是String 需要转换成相应类型
					objects.add(ConvertUtils.convert(map.get(paramterName.get(i)), entry));
				} else {
					if (entry.getName().indexOf("HttpContext") == -1) {
						objects.add(MapToBean.convertMap(entry, map));
					} else {
						objects.add(context);
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}

		}
		return objects.toArray(array);
	}

	/**
	 * 执行拦截器chain
	 * 
	 * @param context
	 * @return
	 * @throws Exception
	 */
	public static List<Object> interceptor(HttpContext context, String order) throws Exception {
		Set<Object> classSet = SpringContext.getInterceSet();// 需要根据weight排序
		List<Object> lists = new ArrayList<>(classSet);
		if (order == "1") {// 正序
			Collections.sort(lists, new Comparator<Object>() {
				@Override
				public int compare(Object findweight, Object findweights) {
					Class<?> findClass1 = findweight.getClass();
					Class<?> findClass2 = findweights.getClass();
					Interceptorn weight1 = findClass1.getAnnotation(Interceptorn.class);
					Interceptorn weight2 = findClass2.getAnnotation(Interceptorn.class);
					return weight1.weight() - weight2.weight();
				}
			});
		} else {
			Collections.sort(lists, new Comparator<Object>() {
				@Override
				public int compare(Object findweight, Object findweights) {
					Class<?> findClass1 = findweight.getClass();
					Class<?> findClass2 = findweights.getClass();
					Interceptorn weight1 = findClass1.getAnnotation(Interceptorn.class);
					Interceptorn weight2 = findClass2.getAnnotation(Interceptorn.class);
					return weight2.weight() - weight1.weight();
				}
			});
		}
		return lists;
	}

	/**
	 * 执行preHandler方法
	 * 
	 * @param context
	 * @param interceptors
	 * @return
	 * @throws Exception
	 */
	public static boolean applyPreHandle(HttpContext context, List<Object> interceptors) throws Exception {
		if (!ObjectUtils.isEmpty(interceptors)) {
			for (Object enrty : interceptors) {
				Method method = ReflectionUtils.findMethod(enrty.getClass(), "preHandle", HttpContext.class);
				// 循环遍历参数类型，将map中的值转成bean
				Object invokeMethod = ReflectionUtils.invokeMethod(method, enrty, context);
				if (!(boolean) invokeMethod) {
					return false;
				}
			}
		}
		return true;
	}

	// 匹配url
	public static boolean getMatch(List<Object> interceptors, HttpContext context) {
		boolean result = true;
		if (!ObjectUtils.isEmpty(interceptors)) {
			for (Object enrty : interceptors) {
				Interceptorn annotation = enrty.getClass().getAnnotation(Interceptorn.class);
				String[] exclud = annotation.exclud();
				for (String entry : exclud) {
					PathMatcher matcher = new AntPathMatcher();
					result = matcher.match(entry, context.getUri());
				}
			}
		}
		return result;
	}

	/**
	 * 
	 * @param context
	 * @param interceptors
	 * @return
	 * @throws Exception
	 */
	public static void afterCompletion(HttpContext context, List<Object> interceptors) throws Exception {
		if (!ObjectUtils.isEmpty(interceptors)) {
			for (Object enrty : interceptors) {
				Method method = ReflectionUtils.findMethod(enrty.getClass(), "afterCompletion", HttpContext.class);
				// 循环遍历参数类型，将map中的值转成bean
				ReflectionUtils.invokeMethod(method, enrty, context);
			}
		}
	}

	/**
	 * 获取参数名字
	 * 
	 * @param clazz
	 * @param methodName
	 * @return
	 */
	public static List<String> getParamterName(Class<? extends Object> clazz, String methodName) {
		LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
		Method[] methods = clazz.getDeclaredMethods();
		for (Method method : methods) {
			if (methodName.equals(method.getName())) {
				String[] params = u.getParameterNames(method);
				return Arrays.asList(params);
			}
		}

		return null;
	}

}
